{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c8d70ae6",
   "metadata": {},
   "source": [
    "# 消息定义"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1339e348",
   "metadata": {},
   "source": [
    "消息是 LangChain 中模型的基本上下文单位。它们代表模型的输入和输出，携带与 LLM 交互时表示对话状态所需的内容和元数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b42a076",
   "metadata": {},
   "source": [
    "消息是包含以下内容的对象：\n",
    " 角色 - 标识消息类型（例如 ，systemuser）\n",
    " 内容 - 表示消息的实际内容（如文本、图像、音频、文档等）\n",
    " 元数据 - 可选字段，例如响应信息、消息 ID 和令牌使用情况\n",
    "LangChain 提供了一种适用于所有模型提供程序的标准消息类型，无论调用何种模型，都能确保一致的行为。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29986c43",
   "metadata": {},
   "source": [
    "使用消息的最简单方法是创建消息对象并在调用时将它们传递给模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "2b96e4e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import init_chat_model\n",
    "from langchain.messages import HumanMessage, AIMessage, SystemMessage\n",
    "\n",
    "model = init_chat_model(\"gpt-5-nano\")\n",
    "\n",
    "system_msg = SystemMessage(\"You are a helpful assistant.\")\n",
    "human_msg = HumanMessage(\"Hello, how are you?\")\n",
    "\n",
    "# Use with chat models\n",
    "messages = [system_msg, human_msg]\n",
    "response = model.invoke(messages)  # Returns AIMessage"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f95675b9",
   "metadata": {},
   "source": [
    "# 系统消息(SystemMessage)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a1d76df",
   "metadata": {},
   "source": [
    "SystemMessage 表示初始指令，用于设定模型的行为、角色和响应准则。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "9d166cac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SystemMessage(content='你是一个专业的编程助手', additional_kwargs={}, response_metadata={})"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.messages import SystemMessage\n",
    "# 其实他是指向 \n",
    "from langchain_core.messages import SystemMessage\n",
    "# 设定助手角色\n",
    "system_msg = SystemMessage(content=\"你是一个专业的编程助手\")\n",
    "system_msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "0eb8d41f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SystemMessage(content='\\n# 客户服务助手系统提示词\\n你是专业的客户服务助手，核心工作是高效、妥善响应用户咨询，具体要求如下：\\n\\n## 核心职责\\n1. 沟通保持礼貌得体，回复专业且有条理\\n2. 面对不确定的问题，直接说明无法确认，不随意猜测\\n3. 语言简洁明了，聚焦用户需求，避免冗余表述\\n\\n## 严格限制\\n1. 不提供任何医疗相关建议（包括病症判断、用药指导等）\\n2. 不泄露或分享任何个人信息（含自身及用户信息）\\n3. 不涉及超出客服范畴的无关话题，不发表主观评价\\n\\n要不要我帮你生成一份**场景化客服回复模板**，涵盖常见咨询场景的标准话术？\\n', additional_kwargs={}, response_metadata={})"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 详细的系统提示词\n",
    "system_msg = SystemMessage(content=\"\"\"\n",
    "# 客户服务助手系统提示词\n",
    "你是专业的客户服务助手，核心工作是高效、妥善响应用户咨询，具体要求如下：\n",
    "\n",
    "## 核心职责\n",
    "1. 沟通保持礼貌得体，回复专业且有条理\n",
    "2. 面对不确定的问题，直接说明无法确认，不随意猜测\n",
    "3. 语言简洁明了，聚焦用户需求，避免冗余表述\n",
    "\n",
    "## 严格限制\n",
    "1. 不提供任何医疗相关建议（包括病症判断、用药指导等）\n",
    "2. 不泄露或分享任何个人信息（含自身及用户信息）\n",
    "3. 不涉及超出客服范畴的无关话题，不发表主观评价\n",
    "\n",
    "要不要我帮你生成一份**场景化客服回复模板**，涵盖常见咨询场景的标准话术？\n",
    "\"\"\")\n",
    "system_msg"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c68e4de",
   "metadata": {},
   "source": [
    "```python\n",
    "class SystemMessage(BaseMessage):\n",
    "    def __init__(\n",
    "        self,\n",
    "        content: str | list[str | dict] | None = None, #  核心作用：存储消息的主要内容，支持多种数据格式：\n",
    "        content_blocks: list[types.ContentBlock] | None = None, # 核心作用：用于存储结构化的内容块（基于 LangChain 的 ContentBlock 类型），通常用于多模态场景（如文本 + 图片、文本 + 文件等）。\n",
    "        **kwargs: Any, # 核心作用：接收任意额外参数（如 name、id、metadata 等），用于扩展消息的元信息。\n",
    "    ) -> None: \n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "de3e75f8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SystemMessage(content='你是旅游客服，负责解答景区门票和交通问题', additional_kwargs={}, response_metadata={}, version='v2.1', effective_date='2025-11-01', applicable_scenario='国内景区咨询')"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 带元信息的系统提示（用于版本管理或场景标记）\n",
    "meta_system_msg = SystemMessage(\n",
    "    content=\"你是旅游客服，负责解答景区门票和交通问题\",\n",
    "    version=\"v2.1\",  # 自定义：提示词版本\n",
    "    effective_date=\"2025-11-01\",  # 自定义：生效时间\n",
    "    applicable_scenario=\"国内景区咨询\"  # 自定义：场景标记\n",
    ")\n",
    "meta_system_msg"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9588ce24",
   "metadata": {},
   "source": [
    "# 用户消息(HumanMessage)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90650275",
   "metadata": {},
   "source": [
    "HumanMessage (用户消息)\n",
    "表示用户输入和交互，可以包含文本、图像、音频、文件等多模态内容。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "dc00c6da",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content='你好，我是 Bob', additional_kwargs={}, response_metadata={})"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.messages import HumanMessage\n",
    "# from langchain_core.messages import HumanMessage\n",
    "# 1. 基础文本消息：仅包含用户输入内容\n",
    "basic_human_msg = HumanMessage(content=\"你好，我是 Bob\")\n",
    "basic_human_msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "e4c701de",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content='帮我分析这个数据', additional_kwargs={}, response_metadata={}, id='msg_123')"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 2. 带消息ID的消息：用于消息追踪或唯一标识\n",
    "identified_human_msg = HumanMessage(\n",
    "    content=\"帮我分析这个数据\",\n",
    "    id=\"msg_123\"  # 自定义消息ID，可用于日志记录或对话管理\n",
    ")\n",
    "identified_human_msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "e0f0a5aa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content='这是我的问题', additional_kwargs={}, response_metadata={'user_id': 'user_456', 'session': 'abc', 'timestamp': '2025-11-10 10:00:00'})"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 3. 带元数据的消息：附加用户相关上下文信息\n",
    "meta_human_msg = HumanMessage(\n",
    "    content=\"这是我的问题\",\n",
    "    response_metadata={\n",
    "        \"user_id\": \"user_456\",  # 用户唯一标识\n",
    "        \"session\": \"abc\",       # 会话ID，用于关联同一轮对话\n",
    "        \"timestamp\": \"2025-11-10 10:00:00\"  # 可补充时间戳等信息\n",
    "    }\n",
    ")\n",
    "meta_human_msg"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4a03e98",
   "metadata": {},
   "source": [
    "源码初始化解析\n",
    "```python\n",
    "class HumanMessage(BaseMessage):\n",
    "    def __init__(\n",
    "        self,\n",
    "        content: str | list[str | dict] | None = None, #  核心作用：存储消息的主要内容，支持多种数据格式：\n",
    "        content_blocks: list[types.ContentBlock] | None = None,  # 核心作用：用于存储结构化的内容块（基于 LangChain 的 ContentBlock 类型），通常用于多模态场景（如文本 + 图片、文本 + 文件等）。\n",
    "        **kwargs: Any, # 核心作用：接收任意额外参数（如 name、id、metadata 等），用于扩展消息的元信息。\n",
    "    ) -> None:\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "ee447d40",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content='Hello!', additional_kwargs={}, response_metadata={}, name='alice', id='msg_123')"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 所以你只要传入**kwargs都行。 字段名=字段内容\n",
    "human_msg = HumanMessage(\n",
    "    content=\"Hello!\",\n",
    "    name=\"alice\",  # Optional: identify different users\n",
    "    id=\"msg_123\",  # Optional: unique identifier for tracing\n",
    ")\n",
    "human_msg"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f971b75",
   "metadata": {},
   "source": [
    "# AI消息(AIMessage)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "bcce20a6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='你好 Bob！我能帮你什么吗？', additional_kwargs={}, response_metadata={})"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.messages import AIMessage\n",
    "# from langchain_core.messages import AIMessage\n",
    "# 简单 AI 响应\n",
    "ai_message = AIMessage(content=\"你好 Bob！我能帮你什么吗？\")\n",
    "ai_message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "3bca9ca3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ai_message content='' additional_kwargs={} response_metadata={} tool_calls=[{'name': 'get_weather', 'args': {'location': '北京'}, 'id': 'call_123', 'type': 'tool_call'}]\n",
      "ai_message.content \n",
      "ai_message.tool_calls [{'name': 'get_weather', 'args': {'location': '北京'}, 'id': 'call_123', 'type': 'tool_call'}]\n"
     ]
    }
   ],
   "source": [
    "# 包含工具调用的ai_message\n",
    "ai_message = AIMessage(\n",
    "    content=\"\",\n",
    "    tool_calls=[\n",
    "        {\n",
    "            \"name\": \"get_weather\",\n",
    "            \"args\": {\"location\": \"北京\"},\n",
    "            \"id\": \"call_123\"\n",
    "        }\n",
    "    ]\n",
    ")\n",
    "print(\"ai_message\", ai_message)\n",
    "print(\"ai_message.content\", ai_message.content)\n",
    "print(\"ai_message.tool_calls\", ai_message.tool_calls)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab3b9657",
   "metadata": {},
   "source": [
    "AIMessage 的特殊属性:\n",
    "\n",
    "text: 模型生成的文本内容 string\n",
    "\n",
    "content: 消息原本的内容 string | dict[]\n",
    "\n",
    "content_blocks: 消息的标准化内容块 ContentBlock[]\n",
    "\n",
    "tool_calls: 工具调用列表 dict[] | None\n",
    "\n",
    "id: 消息的唯一标识符 string\n",
    "\n",
    "usage_metadata: 消息的使用元数据，其中包含 Token 使用信息 dict | None\n",
    "\n",
    "response_metadata: 消息的响应元数据 ResponseMetadata | None\n",
    "\n",
    "使用场景:\n",
    "\n",
    "模型生成的回答\n",
    "\n",
    "工具调用请求\n",
    "\n",
    "手动插入对话历史\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b6a4fc6f",
   "metadata": {},
   "source": [
    "# 工具消息(ToolMessage)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "46e5ce3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "工具结果: 5\n",
      "关联的调用ID: call_001\n",
      "执行状态: success\n"
     ]
    }
   ],
   "source": [
    "from langchain.messages import ToolMessage\n",
    "# from langchain_core.messages import ToolMessage\n",
    "\n",
    "# 假设工具调用的ID是 \"call_001\"，调用\"加法工具\"返回结果\"5\"\n",
    "tool_msg = ToolMessage(\n",
    "    content=\"5\",  # 工具返回的结果（会传给模型）\n",
    "    tool_call_id=\"call_001\"  # 关联之前的工具调用ID\n",
    ")\n",
    "\n",
    "print(\"工具结果:\", tool_msg.content)  # 输出：5\n",
    "print(\"关联的调用ID:\", tool_msg.tool_call_id)  # 输出：call_001\n",
    "print(\"执行状态:\", tool_msg.status)  # 输出：success（默认成功）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "b0906f49",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "错误信息: 错误：缺少参数'城市'，无法查询天气\n",
      "执行状态: error\n"
     ]
    }
   ],
   "source": [
    "from langchain.messages import ToolMessage\n",
    "\n",
    "# 假设工具调用ID是 \"call_002\"，调用\"查询天气工具\"时参数错误\n",
    "tool_msg = ToolMessage(\n",
    "    content=\"错误：缺少参数'城市'，无法查询天气\",  # 错误信息\n",
    "    tool_call_id=\"call_002\",  # 关联失败的调用ID\n",
    "    status=\"error\"  # 标记为错误状态\n",
    ")\n",
    "\n",
    "print(\"错误信息:\", tool_msg.content)\n",
    "print(\"执行状态:\", tool_msg.status)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "8401ae75",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "传给模型的内容: 浦发银行（600000）当前价格：8.5元，涨跌幅+1.2%\n",
      "完整原始数据: {'股票代码': '600000', '名称': '浦发银行', '当前价格': 8.5, '今日涨跌幅': '+1.2%', '历史数据': [7.8, 8.2, 8.3, 8.5]}\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import ToolMessage\n",
    "\n",
    "# 假设工具调用ID是 \"call_003\"，调用\"股票查询工具\"返回详细数据\n",
    "full_tool_output = {\n",
    "    \"股票代码\": \"600000\",\n",
    "    \"名称\": \"浦发银行\",\n",
    "    \"当前价格\": 8.5,\n",
    "    \"今日涨跌幅\": \"+1.2%\",\n",
    "    \"历史数据\": [7.8, 8.2, 8.3, 8.5]  # 额外的详细数据\n",
    "}\n",
    "\n",
    "# 只将关键信息传给模型，完整数据存在artifact中\n",
    "tool_msg = ToolMessage(\n",
    "    content=\"浦发银行（600000）当前价格：8.5元，涨跌幅+1.2%\",  # 传给模型的简化内容\n",
    "    tool_call_id=\"call_003\",\n",
    "    artifact=full_tool_output  # 存储完整原始数据（供后续其他逻辑使用）\n",
    ")\n",
    "\n",
    "print(\"传给模型的内容:\", tool_msg.content)\n",
    "print(\"完整原始数据:\", tool_msg.artifact)  # 可获取历史数据等详细信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "7dbc72c6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "call_004的结果: 北京今天天气：晴，25℃\n",
      "call_005的结果: Hello, world!\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import ToolMessage\n",
    "# 多工具调用\n",
    "# 模型同时调用了两个工具：\"天气查询\"（ID: call_004）和\"翻译\"（ID: call_005）\n",
    "\n",
    "# 天气工具的响应\n",
    "weather_msg = ToolMessage(\n",
    "    content=\"北京今天天气：晴，25℃\",\n",
    "    tool_call_id=\"call_004\"\n",
    ")\n",
    "\n",
    "# 翻译工具的响应\n",
    "translate_msg = ToolMessage(\n",
    "    content=\"Hello, world!\",  # 翻译结果\n",
    "    tool_call_id=\"call_005\"\n",
    ")\n",
    "\n",
    "# 模型可通过tool_call_id匹配对应的请求和响应\n",
    "print(f\"call_004的结果: {weather_msg.content}\")  # 输出：北京今天天气...\n",
    "print(f\"call_005的结果: {translate_msg.content}\")  # 输出：Hello, world!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "303f889e",
   "metadata": {},
   "source": [
    "tool_call_id：唯一标识，用于关联工具调用请求和响应（关键属性）。\n",
    "\n",
    "content：传给模型的内容（通常是工具结果的简化 / 文本形式）。\n",
    "\n",
    "status：标记工具执行状态（success/error）。\n",
    "\n",
    "artifact：存储完整原始结果（非必须，用于需要详细数据的场景）。\n",
    "\n",
    "\n",
    "使用场景:\n",
    "\n",
    "返回工具执行结果\n",
    "\n",
    "报告工具执行错误\n",
    "\n",
    "提供额外的工具元数据"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f155c4f",
   "metadata": {},
   "source": [
    "# 删除消息(RemoveMessage)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db8480f1",
   "metadata": {},
   "source": [
    "RemoveMessage 是用于删除聊天历史中特定消息的类，核心作用是通过目标消息的 id 来指定要移除的消息。常用于内存管理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ab4d63fc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "删除前的历史: ['你好', '您好！有什么可以帮您？']\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import RemoveMessage, HumanMessage, AIMessage\n",
    "# 仅仅是标记那条消息被删除了\n",
    "# 模拟聊天历史（包含不同类型的消息，每个消息有唯一id）\n",
    "chat_history = [\n",
    "    HumanMessage(content=\"你好\", id=\"msg_001\"),  # 用户消息，id为msg_001\n",
    "    AIMessage(content=\"您好！有什么可以帮您？\", id=\"msg_002\")  # AI消息，id为msg_002\n",
    "]\n",
    "\n",
    "print(\"删除前的历史:\", [msg.content for msg in chat_history])\n",
    "# 输出：['你好', '您好！有什么可以帮您？']\n",
    "# 创建一个RemoveMessage，指定要删除的消息id为\"msg_001\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "36416e77",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "删除后的历史: ['你好', '您好！有什么可以帮您？']\n"
     ]
    }
   ],
   "source": [
    "remove_msg = RemoveMessage(id=\"msg_001\")\n",
    "\n",
    "# 模拟删除逻辑（实际中可能由框架处理，这里手动过滤）\n",
    "# new_chat_history = [msg for msg in chat_history if msg.id != remove_msg.id]\n",
    "new_chat_history = [msg for msg in chat_history]\n",
    "\n",
    "print(\"删除后的历史:\", [msg.content for msg in new_chat_history])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "8bf66481",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "错误信息: RemoveMessage does not support 'content' field.\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import RemoveMessage\n",
    "# 尝试传递 content 参数（会报错）\n",
    "try:\n",
    "    # 错误示例：尝试传递content参数\n",
    "    RemoveMessage(id=\"msg_003\", content=\"删除这条\")\n",
    "except ValueError as e:\n",
    "    print(\"错误信息:\", str(e))  # 输出：RemoveMessage does not support 'content' field."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "22d5c4d7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "清理后的历史: ['北京天气如何？', '北京今天25℃，晴']\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import RemoveMessage, HumanMessage, AIMessage\n",
    "\n",
    "# 模拟多轮对话历史\n",
    "chat_history = [\n",
    "    HumanMessage(content=\"北京天气如何？\", id=\"msg_004\"),\n",
    "    AIMessage(content=\"正在查询...\", id=\"msg_005\"),  # 临时中间消息，后续可删除\n",
    "    AIMessage(content=\"北京今天25℃，晴\", id=\"msg_006\")\n",
    "]\n",
    "\n",
    "# 创建RemoveMessage删除临时消息（id=msg_005）\n",
    "remove_temp_msg = RemoveMessage(id=\"msg_005\")\n",
    "# 执行删除\n",
    "clean_history = [msg for msg in chat_history if msg.id != remove_temp_msg.id]\n",
    "print(\"清理后的历史:\", [msg.content for msg in clean_history])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b758b85",
   "metadata": {},
   "source": [
    "# Message Content(消息内容)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0e43ee5",
   "metadata": {},
   "source": [
    "内容类型\n",
    "\n",
    "消息的 content 属性是松散类型的，支持多种格式：\n",
    "\n",
    "字符串: 简单的文本内容\n",
    "\n",
    "提供商原生格式: 如 OpenAI 格式的内容块列表\n",
    "\n",
    "LangChain 标准内容块: 跨提供商的统一格式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aa273989",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "# 1. 字符串内容\n",
    "msg1 = HumanMessage(content=\"你好，世界！\")\n",
    "\n",
    "# 2. “提供商” 原生格式 (OpenAI)\n",
    "msg2 = HumanMessage(content=[\n",
    "    {\"type\": \"text\", \"text\": \"描述这张图片\"},\n",
    "    {\n",
    "        \"type\": \"image_url\", \n",
    "        \"image_url\": {\"url\": \"https://example.com/image.jpg\"}\n",
    "    }\n",
    "])\n",
    "\n",
    "# 3. LangChain 标准内容块（一种跨提供商通用的“标准表示形式”。）\n",
    "msg3 = HumanMessage(content_blocks=[\n",
    "    {\"type\": \"text\", \"text\": \"描述这张图片\"},\n",
    "    {\"type\": \"image\", \"url\": \"https://example.com/image.jpg\"}\n",
    "])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16b116f0",
   "metadata": {},
   "source": [
    "# 标准内容块(content_blocks)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8e77c62",
   "metadata": {},
   "source": [
    "content: 松散类型，支持字符串和任意对象列表\n",
    "\n",
    "content_blocks（标准形式）: 类型安全的接口，使用 LangChain 标准内容块"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6db7fa33",
   "metadata": {},
   "source": [
    "消息对象实现了一个属性，会延迟将 content 属性解析为标准化、类型安全的表示形式。\n",
    "\n",
    "例如，从 ChatAnthropic 或 ChatOpenAI 生成的消息，会包含对应提供商格式的 content_blocks 内容块或 thinking/reasoning 相关块，但可通过延迟解析，统一转换为一致的 ReasoningContentBlock 表示形式。\n",
    "\n",
    "补充说明：\n",
    "\n",
    "关键术语保留原英文（如 content_blocks、ReasoningContentBlock），符合技术文档翻译习惯，避免歧义。\n",
    "\n",
    "“lazily parse” 译为 “延迟解析”，指需要时才执行解析操作，而非初始化时立即解析。\n",
    "\n",
    "“type-safe” 译为 “类型安全”，是编程术语，指通过类型检查确保数据类型正确。\n",
    "\n",
    "“thinking/reasoning” 译为 “相关块”，结合上下文指提供商返回的 “思考过程” 类结构化数据，与后文 ReasoningContentBlock 对应。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "629d4c91",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'type': 'reasoning',\n",
       "  'reasoning': '...',\n",
       "  'extras': {'signature': 'WaUjzkyp...'}},\n",
       " {'type': 'text', 'text': '...'}]"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.messages import AIMessage\n",
    "# Anthropic\n",
    "message = AIMessage(\n",
    "    content=[\n",
    "        {\"type\": \"thinking\", \"thinking\": \"...\", \"signature\": \"WaUjzkyp...\"},\n",
    "        {\"type\": \"text\", \"text\": \"...\"},\n",
    "    ],\n",
    "    response_metadata={\"model_provider\": \"anthropic\"}\n",
    ")\n",
    "message.content_blocks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fb707ed5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'type': 'reasoning', 'id': 'rs_abc123', 'reasoning': '摘要内容一：用户问的是天气'},\n",
       " {'type': 'reasoning', 'id': 'rs_abc123', 'reasoning': '摘要内容二：需要调用天气工具'},\n",
       " {'type': 'text', 'text': '...', 'id': 'msg_abc123'}]"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.messages import AIMessage\n",
    "# openai的\n",
    "message = AIMessage(\n",
    "    content=[\n",
    "        {\n",
    "            \"type\": \"reasoning\",\n",
    "            \"id\": \"rs_abc123\",\n",
    "            \"summary\": [\n",
    "                {\"type\": \"summary_text\", \"text\": \"摘要内容一：用户问的是天气\"},\n",
    "                {\"type\": \"summary_text\", \"text\": \"摘要内容二：需要调用天气工具\"},\n",
    "            ],\n",
    "        },\n",
    "        {\"type\": \"text\", \"text\": \"...\", \"id\": \"msg_abc123\"},\n",
    "    ],\n",
    "    response_metadata={\"model_provider\": \"openai\"}\n",
    ")\n",
    "message.content_blocks"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef35a475",
   "metadata": {},
   "source": [
    "# 多模态内容（Multimodal 多模态）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e0154b0",
   "metadata": {},
   "source": [
    "多模态指能够处理文本、音频、图像、视频等不同形式数据的能力。LangChain 包含了适用于这些数据的标准类型，可跨提供商通用。\n",
    "\n",
    "聊天模型可以接收多模态数据作为输入，也能生成多模态数据作为输出。以下是包含多模态数据的输入消息简短示例。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "c8546e49",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content=[{'type': 'text', 'text': '这张图片里有什么?'}, {'type': 'image_url', 'image_url': {'url': 'https://example.com/image.jpg'}}], additional_kwargs={}, response_metadata={})"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 图像输入\n",
    "from langchain.messages import HumanMessage\n",
    "\n",
    "msg = HumanMessage(\n",
    "    content=[\n",
    "        {\"type\": \"text\", \"text\": \"这张图片里有什么?\"},\n",
    "        {\n",
    "            \"type\": \"image_url\",\n",
    "            \"image_url\": {\"url\": \"https://example.com/image.jpg\"}\n",
    "        }\n",
    "    ]\n",
    ")\n",
    "msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "004eb633",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content=[{'type': 'text', 'text': '分析这张图片'}, {'type': 'image_url', 'image_url': {'url': ''}}], additional_kwargs={}, response_metadata={})"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用 Base64 编码\n",
    "import base64\n",
    "from langchain.messages import HumanMessage\n",
    "\n",
    "# 读取并编码图像\n",
    "with open(\"image.png\", \"rb\") as f:\n",
    "    image_data = base64.b64encode(f.read()).decode()\n",
    "\n",
    "msg = HumanMessage(\n",
    "    content=[\n",
    "        {\"type\": \"text\", \"text\": \"分析这张图片\"},\n",
    "        {\n",
    "            \"type\": \"image_url\",\n",
    "            \"image_url\": {\"url\": f\"data:image/jpeg;base64,{image_data}\"}\n",
    "        }\n",
    "    ]\n",
    ")\n",
    "msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "8fb310b2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "HumanMessage(content=[{'type': 'text', 'text': '描述图片内容'}, {'type': 'image', 'source_type': 'id', 'id': 'file-abc123'}], additional_kwargs={}, response_metadata={})"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 通过供应商提供的id文件\n",
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "msg = HumanMessage(\n",
    "    content=[\n",
    "        {\"type\": \"text\", \"text\": \"描述图片内容\"},\n",
    "        {\"type\": \"image\", \"source_type\": \"id\", \"id\": \"file-abc123\"}\n",
    "    ]\n",
    ")\n",
    "\n",
    "msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4fce2ac1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# From URL\n",
    "message_url = {\n",
    "    \"role\": \"user\",\n",
    "    \"content\": [\n",
    "        {\"type\": \"text\", \"text\": \"描述这份文档的内容。\"},\n",
    "        {\"type\": \"file\", \"url\": \"https://example.com/path/to/document.pdf\"},\n",
    "    ]\n",
    "}\n",
    "print(\"message_url\", message_url)\n",
    "# From base64 data\n",
    "base64_message = {\n",
    "    \"role\": \"user\",\n",
    "    \"content\": [\n",
    "        {\"type\": \"text\", \"text\": \"描述这份文档的内容。\"},\n",
    "        {\n",
    "            \"type\": \"file\",\n",
    "            \"base64\": \"AAAAIGZ0eXBtcDQyAAAAAGlzb21tcDQyAAACAGlzb2...\",\n",
    "            \"mime_type\": \"application/pdf\",\n",
    "            # \"mime_type\": \"audio/wav\",\n",
    "            # \"mime_type\": \"video/mp4\",\n",
    "\n",
    "        },\n",
    "    ]\n",
    "}\n",
    "print(\"base64_message\", base64_message)\n",
    "# From provider-managed File ID\n",
    "message = {\n",
    "    \"role\": \"user\",\n",
    "    \"content\": [\n",
    "        {\"type\": \"text\", \"text\": \"描述这份文档的内容。\"},\n",
    "        {\"type\": \"file\", \"file_id\": \"file-abc123\"},\n",
    "    ]\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-course (3.10.12)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
